package com.flanz.apache.velocity.uimocker;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
import org.apache.velocity.runtime.resource.util.StringResourceRepository;

import com.google.gson.Gson;

/**
 * VmGeneratorMain.
 * 
 * @author cf
 * 
 */
public class VmGeneratorMain {

	public static void main(String[] args) throws Exception {
		ByteArrayOutputStream b = new ByteArrayOutputStream();
		Properties APP = new Properties();
		String marocFileList = "" ; // disable
		generateHtml(APP, "./demo", "templates/index.vm", marocFileList, b);
		System.out.print(b);
	}

	public static void generateHtml(Properties vm_app_vars,
			String filesReproOnDisc,String globalMacrosFileList, String templateName, OutputStream out)
			throws Exception {
		/* first, get and initialize an engine */
		VelocityEngine ve = new VelocityEngine();

		// VelocityEngine System Setting
		ve.setProperty("resource.loader", "file");
		ve.setProperty("input.encoding", "utf-8");

		ve.setProperty("resource.loader", "string");
		ve.setProperty("string.resource.loader.description",
				"Velocity StringResource loader");
		ve.setProperty("string.resource.loader.class",
				"org.apache.velocity.runtime.resource.loader.StringResourceLoader");
		ve.setProperty("string.resource.loader.repository.class",
				"org.apache.velocity.runtime.resource.util.StringResourceRepositoryImpl");
		ve.setProperty("eventhandler.include.class",
				"com.flanz.apache.velocity.uimocker.IncludeRelativePath");
		ve.setProperty("eventhandler.include.class",
				"com.flanz.apache.velocity.uimocker.MyIncludeRelativePath");
		ve.init();
		StringResourceRepository repo = StringResourceLoader.getRepository();
		DiscResourceCollector discResourceCollector = new DiscResourceCollector(
				filesReproOnDisc);

		// TODO move interfaces, to magic
		MacrosCollector mc = new MacrosCollector(discResourceCollector, globalMacrosFileList);
		mc.putMacrosToRepro(discResourceCollector);
		Map<String, String> resolveAll = discResourceCollector.resolveAll();

		Set<Entry<String, String>> entrySet = resolveAll.entrySet();
		Iterator iterator = entrySet.iterator();
		// first handle all VM
		while (iterator.hasNext()) {
			Entry<String, String> entry = (Entry<String, String>) iterator
					.next();
			if (entry.getKey().endsWith(".vm")) {
				System.out.println("GENVM: " + entry.getKey());
				handleVmFile(vm_app_vars, ve, repo, resolveAll, entry);
			}
		}

		// first handle all JSON
		iterator = entrySet.iterator();
		while (iterator.hasNext()) {
			Entry<String, String> entry = (Entry<String, String>) iterator
					.next();

			if (entry.getKey().endsWith(".json")
					&& !entry.getKey().startsWith("templates/")) {
				System.out.println("GENJSON: " + entry.getKey());
				handleJson(vm_app_vars, ve, repo, resolveAll, entry);
			}
		}

		System.out.println("STARTMAIN-TEMPLATE: " + templateName);

		/* next, get the Template */
		Template t = ve.getTemplate(templateName);

		/* create a context and add data */
		VelocityContext context = new VelocityContext();
		context.put("APP", vm_app_vars);
		/* now render the template into a StringWriter */
		StringWriter writer = new StringWriter();
		t.merge(context, writer);
		/* show the World */

		String result = writer.toString().toString();
		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
				result.getBytes("UTF-8"));
		IOUtils.copy(byteArrayInputStream, out);

	}

	private static void handleJson(Properties vm_app_vars, VelocityEngine ve,
			StringResourceRepository repo, Map<String, String> resolveAll,
			Entry<String, String> entry) {
		System.out.println(entry.getKey());
		int lastIndexOf = entry.getKey().lastIndexOf("/");
		if (lastIndexOf == -1) {
			return;
		}
		String fileName = entry.getKey().substring(lastIndexOf + 1);
		String pathToFile = entry.getKey().substring(0, lastIndexOf);

		String[] split = fileName.split("[.]");
		if (split != null && split.length > 2) {
			// Add Merge
			String partName = split[1];
			if (split.length > 3) {
				String[] copyOfRange = Arrays.copyOfRange(split, 1,
						split.length - 1);
				StringBuilder sb = new StringBuilder();
				for (int i = 0; i < copyOfRange.length; i++) {
					if (i != 0) {
						sb.append('.');
					}
					sb.append(copyOfRange[i]);
				}
				// Input: selection-pattern-dynamic.short.active.json
				// Output: short.active
				partName = sb.toString();
			}

			String vmFileName = pathToFile + "/" + split[0] + ".vm";

			String stringVM = resolveAll.get(vmFileName);

			if (stringVM != null) {
				repo.putStringResource(vmFileName, stringVM);
				/**
				 * apps Orginal VM \\apps Expand VM
				 */
				String vmResult = generateText(vm_app_vars, ve, vmFileName,
						entry.getValue());
				System.out.println(vmResult);
				String partVmKeyName = makePartVmKey(partName, vmFileName);
				putProcessedVmToVmRepro(repo, partVmKeyName, vmResult);
			}
		}
		System.out.println(entry.getKey());
	}

	public static String makePartVmKey(String partName, String orgVMPath) {
		String partVmKeyName = null;
		String start_slash = orgVMPath.startsWith("/") ? "" : "/";

		partVmKeyName = "PARTDATA_" + partName + start_slash
				+ orgVMPath.replace(".json", ".vm");

		return partVmKeyName;
	}

	private static void handleVmFile(Properties vm_app_vars, VelocityEngine ve,
			StringResourceRepository repo, Map<String, String> resolveAll,
			Entry<String, String> entry) {
		// Load Default
		/**
		 * apps Orginal VM \\apps Expand VM
		 */

		// Fix #include to parse
		// String vm_with_parse = entry.getValue() ;
		String vm_with_parse = StringUtils.replace(entry.getValue(),
				"#include(", "#parse(");
		String vmAbsPath = entry.getKey();
		repo.putStringResource(vmAbsPath, vm_with_parse);

		String value = entry.getValue();
		// Get Json , default case
		String jsonKey = vmAbsPath.replace(".vm", ".json");
		String jsonString = resolveAll.get(jsonKey);
		if (jsonString == null) {
			System.out.println("NO default JSON found:" + jsonKey);
		}
		if (!vmAbsPath.startsWith("templates/")) {

			/**
			 * apps Orginal VM \\apps Expand VM
			 */
			String vmResult = generateText(vm_app_vars, ve, vmAbsPath,
					jsonString);
			putProcessedVmToVmRepro(repo, vmAbsPath, vmResult);

		} else {
			System.out.println("UNKNOW VM CASE default JSON:" + jsonKey);
			repo.putStringResource("/" + vmAbsPath,
					"## VM+PLAIN \n" + entry.getValue());
		}
	}

	private static String generateText(Properties vm_app_vars,
			VelocityEngine ve, String vmAbsPath, String jsonString) {
		HashMap jsonValueMap = new HashMap();
		Gson gson = new Gson();
		if (jsonString != null) {
			jsonValueMap = gson.fromJson(jsonString, jsonValueMap.getClass());
		}
		Template t = ve.getTemplate(vmAbsPath);
		/* create a context and add data */
		jsonValueMap.put("APP", vm_app_vars);
		VelocityContext velocityContext = new VelocityContext(jsonValueMap);
		StringWriter writer = new StringWriter();
		t.merge(velocityContext, writer);
		// repo.putStringResource(key, "\n ## HELLO \n" +
		String vmResult = writer.toString();
		return vmResult;
	}

	private static void putProcessedVmToVmRepro(StringResourceRepository repo,
			String key, String value) {
		boolean debug = false;
		String newPath = "/" + key;
		System.out.println("PUT_TO_REPRO:" + newPath);
		repo.putStringResource(newPath, value);
	}

}
